Skip to content

Stokes_Constrained: unlock parallel (remove over-conservative serial guard)#240

Merged
lmoresi merged 2 commits into
developmentfrom
bugfix/constrained-parallel
Jun 15, 2026
Merged

Stokes_Constrained: unlock parallel (remove over-conservative serial guard)#240
lmoresi merged 2 commits into
developmentfrom
bugfix/constrained-parallel

Conversation

@lmoresi

@lmoresi lmoresi commented Jun 15, 2026

Copy link
Copy Markdown
Member

What

Removes the serial-only guard on SNES_Stokes_Constrained (the in-saddle Lagrange-multiplier free-slip solver). It is parallel-safe; the guard was over-conservative.

Why it was guarded, and why that was unnecessary

The multiplier is carried as a full-domain field whose interior DOFs are pinned away by _constrain_interior_multipliers_in_section. That reduction is rank-local section surgery — it uses the distributed boundary-label IS (getStratumIS) and iterates the local chart, constraining DOFs in the local PetscSection per rank. So the assembled global system is partition-independent; there is no single-rank enumeration to MPI-decompose.

Validation

Bit-identical serial-vs-parallel (np=1/2/4), isotropic and transverse-isotropic, on the enclosed free-slip annulus:

metric np1 np2 np4
velocity L2(v) 0.39245136 = =
free-slip ∫(v·n)² 1.3e-9 = =
gauge-fixed topography ∫(h−h̄)² 37.0335397 37.0335204 37.0335210

BdIntegral(fn=1) (boundary length) is also bit-identical, confirming the surface integrator is parallel-safe. New regression test: tests/parallel/test_1063_constrained_freeslip_parallel.py (mpi(min_size=2)).

The gauge constant

On an enclosed problem the raw multiplier h is determined only up to the [p,λ] gauge constant, and the solver lands on a partition-dependent representative of it (the velocity and the deviation of h are unaffected). topography() gains a reference="mean" option to subtract the boundary mean for a reproducible, gauge-fixed readout. The default is unchanged (reference=None, raw multiplier) — correct for problems with no gauge freedom (e.g. an open boundary), where the mean of h is the physical mean traction.

Changes

  • src/underworld3/systems/solvers.py: remove the serial guard; add topography(reference=).
  • tests/parallel/test_1063_constrained_freeslip_parallel.py: new np≥2 regression.

Tests

  • Serial constrained suite (test_1061, test_1062): 16 passed (no regression).
  • New parallel test: passes at np2 and np4.

Underworld development team with AI support from Claude Code

…guard)

The in-saddle multiplier free-slip solver was guarded serial-only, but the
interior-multiplier reduction (_constrain_interior_multipliers_in_section) is
rank-local section surgery: it uses the distributed boundary-label IS and
iterates the local chart, so the global system — and hence the velocity solve
and the gauge-invariant boundary traction — are partition-independent.
Validated bit-identical at np=1/2/4 (velocity L2 and mean-stripped boundary
topography) for both isotropic and transverse-isotropic rheology.

On enclosed problems the raw multiplier h carries the [p,lambda] gauge constant
(the solver lands on a partition-dependent representative). topography() gains a
reference="mean" option to subtract the boundary mean for a reproducible,
gauge-fixed readout; the default (reference=None) is unchanged (raw multiplier),
which is correct for problems with no gauge freedom (e.g. an open boundary).

- src/underworld3/systems/solvers.py: remove serial guard; topography(reference=).
- tests/parallel/test_1063_constrained_freeslip_parallel.py: np>=2 regression.

Underworld development team with AI support from Claude Code
Copilot AI review requested due to automatic review settings June 15, 2026 09:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enables running SNES_Stokes_Constrained (the in-saddle Lagrange-multiplier free-slip Stokes solver) under MPI by removing the serial-only guard, and adds a new topography(reference=...) option intended to provide a reproducible (gauge-fixed) topography readout for enclosed problems. It also introduces a parallel regression test to validate partition-independence against stored serial “golden” diagnostics.

Changes:

  • Remove the uw.mpi.size > 1 NotImplementedError guard from SNES_Stokes_Constrained.add_constraint_bc.
  • Add topography(..., reference=None|"mean"), with "mean" subtracting the boundary-mean multiplier via BdIntegral reductions.
  • Add a new MPI regression test (min_size=2) for isotropic and transverse-isotropic cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/underworld3/systems/solvers.py Removes the MPI guard and adds gauge-fixing support to topography(reference=...).
tests/parallel/test_1063_constrained_freeslip_parallel.py Adds a new parallel regression test comparing MPI results to stored serial golden diagnostics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2244 to +2248
problems pass ``reference="mean"`` to subtract the boundary mean and obtain
a gauge-fixed, partition-independent topography. The default
(``reference=None``) returns the raw multiplier — correct for problems with
**no** gauge freedom (e.g. an open boundary), where the mean of :math:`h` is
the physical mean traction and must NOT be removed.
Comment on lines +9 to +11
* the velocity L2 norm (``∫ v·v``) is bit-identical serial vs parallel, and
* the MEAN-STRIPPED boundary topography (``∫(h - h̄)² `` on the constrained
boundary) is bit-identical serial vs parallel.
Comment on lines +80 to +81
"""The parallel solve must reproduce the serial reference: velocity bit-
identical, and the gauge-fixed (mean-stripped) topography bit-identical."""
Comment on lines +69 to +75
blen = float(uw.maths.BdIntegral(
mesh=mesh, fn=sympy.Integer(1), boundary="Upper").evaluate())
hbar = float(uw.maths.BdIntegral(
mesh=mesh, fn=h.sym[0], boundary="Upper").evaluate()) / blen
topo = float(np.sqrt(uw.maths.BdIntegral(
mesh=mesh, fn=(h.sym[0] - hbar) ** 2, boundary="Upper").evaluate()))
return L2, topo
…, wording

- topography() docstring: note that reference="mean" runs collective BdIntegral
  reductions (must be called on all ranks); reference=None is a pure accessor.
- parallel test: compute the gauge-fixed topography via
  solver.topography(boundary, reference="mean") so the new code path is covered.
- test docstring: replace "bit-identical" with "tight tolerance" (the residual
  difference is the parallel reduction order, not the solver); np.isclose
  tolerances are intentional.

Underworld development team with AI support from Claude Code
@lmoresi

lmoresi commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

Addressed the Copilot review in a510445:

  • topography(reference="mean") docstring now notes it runs collective BdIntegral reductions (call on all ranks); reference=None stays a pure symbolic accessor.
  • The parallel test now computes the gauge-fixed topography via solver.topography("Upper", reference="mean"), so that new code path is exercised (golden values unchanged; np2/np4 still pass).
  • Reworded "bit-identical" → "tight tolerance" in the test docstring: the residual serial-vs-parallel difference is the parallel reduction order, not the solver, so the np.isclose tolerances are intentional (velocity 1e-9, topography 1e-6).

@lmoresi lmoresi merged commit 7696d89 into development Jun 15, 2026
1 check passed
@lmoresi lmoresi deleted the bugfix/constrained-parallel branch June 15, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants